use std::{
    cmp::{max, min},
    fmt::{self, Display},
};

use crate::{
    editor::highlighter::{
        Annotation, AnnotationType,
        annotation::{AnnotatedStrIter, AnnotatedStrPart},
    },
    prelude::ByteIdx,
};

// 被注释的字符串，作为 Line 到 View 到 Terminal 之间的传输工具，最终在其中被打印出来。
#[derive(Debug, Default)]
pub struct AnnotatedStr {
    pub string: String,
    pub annotations: Vec<Annotation>,
}

impl AnnotatedStr {
    pub fn from(string: &str) -> Self {
        Self {
            string: String::from(string),
            annotations: Vec::new(),
        }
    }

    pub fn add_annotation(
        &mut self,
        annotation_type: AnnotationType,
        start: ByteIdx,
        end: ByteIdx,
    ) {
        debug_assert!(start <= end);

        self.annotations.push(Annotation {
            annotation_type,
            start,
            end,
        });
    }

    pub fn truncate_left_until(&mut self, until: ByteIdx) {
        self.replace(0, until, "");
    }

    pub fn truncate_right_from(&mut self, from: ByteIdx) {
        self.replace(from, self.string.len(), "");
    }

    // 替换字符串中的部分内容，同时更新注释的位置。
    pub fn replace(&mut self, start: ByteIdx, end: ByteIdx, new_string: &str) {
        // 末尾字节索引不超过原串的长度
        let end = min(end, self.string.len());

        if start >= end {
            // 如果起始位置大于等于结束位置，直接返回
            return;
        }

        // 删除原串中指定范围的部分串，将新串插入中间的空白，新串可以比部分串更长，因为String会自动扩容。
        self.string.replace_range(start..end, new_string);

        // 被替换串的长度
        let replaced_range_len = end.saturating_sub(start);

        // 新字符串长度 < 原字符串长度，说明字符串被缩短了。
        let shortened = new_string.len() < replaced_range_len;
        // 计算新串长度与被替换串的长度差值。
        let len_difference = new_string.len().abs_diff(replaced_range_len);

        // 无长度差异，不需要移动任何注释
        if len_difference == 0 {
            return;
        }

        // 遍历并修改注释
        self.annotations.iter_mut().for_each(|annotation| {
            annotation.start = if annotation.start >= end {
                // 注释的起始位置在插入点的结束位置之后（插入的新串比被替换的旧串更长），就需要将注释向右移动，否则将注释向左移动
                if shortened {
                    annotation.start.saturating_sub(len_difference)
                } else {
                    annotation.start.saturating_add(len_difference)
                }
            } else if annotation.start >= start {
                // 注释的起始位置在插入点的开始位置和结束位置之间（插入的新串比被替换的旧串更短），就需要将注释向左移动
                if shortened {
                    max(start, annotation.start.saturating_sub(len_difference))
                } else {
                    min(end, annotation.start.saturating_add(len_difference))
                }
            } else {
                // 注释的起始位置在插入点的开始位置之前，不需要移动注释
                annotation.start
            };

            annotation.end = if annotation.end >= end {
                if shortened {
                    annotation.end.saturating_sub(len_difference)
                } else {
                    annotation.end.saturating_add(len_difference)
                }
            } else if annotation.end >= start {
                if shortened {
                    max(start, annotation.end.saturating_sub(len_difference))
                } else {
                    min(end, annotation.end.saturating_add(len_difference))
                }
            } else {
                annotation.end
            }
        });

        // retain 会移除所有闭包返回 false 的注释。在这里，我们移除任何现在为空（start == end）或超出字符串范围的注释。
        self.annotations.retain(|annotation| {
            annotation.start < annotation.end && annotation.start < self.string.len()
        });
    }
}

impl Display for AnnotatedStr {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", self.string)
    }
}

impl<'a> IntoIterator for &'a AnnotatedStr {
    type Item = AnnotatedStrPart<'a>;
    type IntoIter = AnnotatedStrIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        AnnotatedStrIter {
            annotated_string: self,
            current_idx: 0,
        }
    }
}
